import { onMounted, type Ref, ref } from 'vue'

interface HookOptions<P = any, D = any> {
  /**
   * 是否自动请求，为true时，将在onMounted钩子自动执行一次getData
   */
  auto?: boolean

  loading?: Ref<boolean>

  params?: P

  success?: (res: D | undefined) => void
}

/**
 * 默认参数
 */
const defaultOptions: HookOptions = {
  auto: false
}

type FunctionParams<T> = T extends (arg: infer P) => any ? P : never

type ApiResult<T extends PromiseFn> = PromiseValue<ReturnType<T>>

type SuccessCallbackFunction<F extends PromiseFn> = (
  res: PromiseValue<ReturnType<F>>
) => void

export default function useRequest<
  API extends PromiseFn,
  D extends FunctionParams<API>
>(api: API, options?: HookOptions<D, ApiResult<API>>) {
  options = { ...defaultOptions, ...options }

  const successFunctions: ((res: SuccessCallbackFunction<API>) => void)[] = []
  const loading = ref(false)
  const data = ref<PromiseValue<ReturnType<API>>>()

  // 切换loading状态
  function toggleLoading(flag: boolean) {
    loading.value = flag
    // 如果设置了自定义loading属性，则一起切换
    if (options?.loading !== undefined) {
      options.loading.value = flag
    }
  }

  // 获取数据
  async function request(params?: Partial<FunctionParams<API>>) {
    toggleLoading(true)
    try {
      data.value = await api({
        ...options?.params,
        ...params
      })
      if (options?.success) {
        options.success(data.value)
      }
      successFunctions.forEach(fn => fn(data.value!))
      return data.value!
    } catch (err) {
      toggleLoading(false)
      throw err
    } finally {
      toggleLoading(false)
    }
  }

  function onSuccess(fn: SuccessCallbackFunction<API>) {
    successFunctions.push(fn)
  }

  onMounted(() => {
    if (options?.auto) {
      request()
    }
  })

  return {
    loading,
    data,
    request,
    onSuccess
  }
}
